#include<SuperOF.h>
using namespace std;

const string version="1.0.03.067";
string OFK_file;
struct Program
{
	bool show_info=true;
	bool show_warning=true;
	bool show_error=true;
	bool debug_mode=false;
	bool ignore_error=false;
	bool program_check=false;
	const int num=6;
};
int main()
{
	Program pg;
	pg.program_check=true; 
	color("cyan-l","black",0,0);
	cout<<"OF-keyboard V"<<version<<endl;
	cout<<"if-OF(c)2020-2021"<<endl;
	color("white","black",0,0);
	line();
	cout<<"Please enter the file path:"<<endl; 
	getline(cin,OFK_file);
	if(strstr(OFK_file.c_str(),":")==NULL)
	{
		OFK_file=work_dir()+"\\"+OFK_file;
		fstream _file;
		_file.open(OFK_file.c_str(),ios::in);
		if(!_file)
		{
			error("Can't find the file.");
			warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 0;
		}
	}
	else
	{
		fstream _file;
		_file.open(OFK_file.c_str(),ios::in);
		if(!_file)
		{
			error("Can't find the file.");
			warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 0;
		}
	}
	FILE*file=fopen(OFK_file.c_str(),"r");
	int size=filelength(fileno(file));
	int count;
	for(count=0;fgetc(file)!=EOF;count++);
	fclose(file);
	info("Loading program...");
	line();
	info("Code length:"+int_to_string(count)+".");
	info("Code size:"+int_to_string(size)+"B.");
	freopen(OFK_file.c_str(),"r",stdin);
	info("Program is running now...");
	line();
	int for_time=0;
	map<char,bool>keydown;
	string key;
	while(1)
	{ 
		if(pg.debug_mode)
			Sleep(1000);
		for_time++;
		bool flag=true;
		int oper_len;
		char operation_c[128]={""};
		string operation="",line_code=""; 
		getline(cin,line_code);
		if(line_code=="END" || line_code=="END;")
			break;
		for(oper_len=0;oper_len<line_code.length();oper_len++)
		{
			if(line_code[oper_len]==' ' || line_code[oper_len]==';')
			{
				flag=false;
				break;
			}
		}
		if(flag)
		{
			if(pg.show_error)
				error("Cannot find operation code in line "+int_to_string(for_time)+".");
			if(pg.show_warning)
				warning("Do you forget to add \" \" or \";\"?");
			if(pg.program_check || pg.ignore_error)
				continue;
			if(pg.show_warning)
				warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 0;
		}
		if(oper_len>=127)
		{
			if(pg.show_error)
				error("Operation code is too long in line "+int_to_string(for_time)+".Expect less than 127,read"+int_to_string(oper_len)+".");
			if(pg.program_check || pg.ignore_error)
				continue;
			if(pg.show_warning)
				warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 0;
		}
		strncpy(operation_c,line_code.c_str(),oper_len);
		operation=operation_c;
		if(pg.show_info)
			info("operation:"+operation+".");
		if(operation=="BEGIN")
		{
			if(pg.show_info)
				info("Program start.");
			if(line_code.length()>6)
			{
				if(pg.show_info)
					info("Parsing parameters...");
				string x=line_code;
				x.erase(0,oper_len+1);
				if(x.length()!=pg.num)
				{
					if(pg.show_error)
						error("BEGIN must has 0 or "+int_to_string(pg.num)+" bool parameters.");
					if(pg.show_warning)
						warning("Parameters in BEGIN will be invalid.");
					continue;
				}
				pg.show_info=x[0]-'0';
				pg.show_warning=x[1]-'0';
				pg.show_error=x[2]-'0';
				pg.debug_mode=x[3]-'0';
				pg.ignore_error=x[4]-'0';
				pg.program_check=x[5]-'0';
			}
			continue;
		}
		if(operation=="KD")
		{
			if(pg.show_info)
				info("Read key down in line "+int_to_string(for_time)+".");
			string x=line_code;
			x.erase(0,oper_len+1);
			if(x.size()!=3 || x[0]!='\'' || x[2]!='\'')
			{
				if(pg.show_error)
					error("KD in line "+int_to_string(for_time)+" needs to add a ASCII character like 'A'.");
				if(pg.program_check || pg.ignore_error)
					continue;
				if(pg.show_warning)
					warning("Program will close in 5 seconds.");
				Sleep(5000);
				return 0;
			}
			if(pg.show_info)
				info("Key \'"+char_to_string(x[1])+"\' was down.");
			if(!pg.program_check)
				keybd_event(x[1],0,0,0);
			key+=char_to_string(x[1]);
			keydown[x[1]]=true;
			continue;
		}
		if(operation=="KU")
		{
			if(pg.show_info)
				info("Read key up in line "+int_to_string(for_time)+".");
			string x=line_code;
			x.erase(0,oper_len+1);
			if(x.size()!=3 || x[0]!='\'' || x[2]!='\'')
			{
				if(pg.show_error)
					error("KU in line "+int_to_string(for_time)+" needs to add a ASCII character like 'A'.");
				if(pg.program_check || pg.ignore_error)
					continue;
				if(pg.show_warning)
					warning("Program will close in 5 seconds.");
				Sleep(5000);
				return 0;
			}
			if(pg.show_info)
				info("Key \'"+char_to_string(x[1])+"\' was up.");
			if(!pg.program_check)
				keybd_event(x[1],0,2,0);
			keydown[x[1]]=false;
			continue;
		}
		if(operation=="KDU")
		{
			if(pg.show_info)
				info("Read key down and up in line "+int_to_string(for_time)+".");
			string x=line_code;
			x.erase(0,oper_len+1);
			for(int i=0;i<x.length();i++)
			{
				if(x[i]==' ')
				{
					if(pg.show_info)
						info("Key ' ' was down and up.");
					if(!pg.program_check)
					{
						keybd_event(0x20,0,0,0);
						keybd_event(0x20,0,2,0);
					}
					keydown[' ']=false;
					continue;
				}
				if(!((x[i]>='A' && x[i]<='Z') || (x[i]>='0' && x[i]<='9') || x[i]=='+' || x[i]=='-' || x[i]==',' || x[i]=='.'))
				{
					if(pg.show_error)
						error("KDU in line "+int_to_string(for_time)+" needs to add A-Z,0-9 or '-' '+' ',' '.' ' ' like \"hello world.\".");
					if(pg.program_check || pg.ignore_error)
						continue;
					if(pg.show_warning)
						warning("Program will close in 5 seconds.");
					Sleep(5000);
					return 0;
				}
				if(x[i]=='+' || x[i]=='-' || x[i]==',' || x[i]=='.')
				{
					map<char,int> amcp;
					amcp['+']=VK_OEM_PLUS;
					amcp['-']=VK_OEM_MINUS;
					amcp[',']=VK_OEM_COMMA;
					amcp['.']=VK_OEM_PERIOD;
					if(pg.show_info)
						info("Key \'"+char_to_string(x[i])+"\' was down and up.");
					if(!pg.program_check)
					{
						keybd_event(amcp[x[i]],0,0,0);
						keybd_event(amcp[x[i]],0,2,0);
					}
					keydown[x[i]]=false;
					continue;
				}
				else
				{
					if(pg.show_info)
						info("Key \'"+char_to_string(x[i])+"\' was down and up.");
					if(!pg.program_check)
					{
						keybd_event(x[i],0,0,0);
						keybd_event(x[i],0,2,0);
					}
					keydown[x[i]]=false;
					continue;
				}
			}
			continue;
		}
		if(operation=="W")
		{
			if(pg.show_info)
				info("Read wait in line "+int_to_string(for_time)+".");
			string x=line_code;
			int num=0;
			x.erase(0,oper_len+1);
			for(int i=0;i<x.length();i++)
			{
				num*=10;
				if(x[i]>='0' && x[i]<='9')
					num+=x[i]-'0';
				else
				{
					if(pg.show_error)
						error("W in line "+int_to_string(for_time)+" needs to add a number like 3000.");
					if(pg.program_check || pg.ignore_error)
						continue;
					if(pg.show_warning)
						warning("Program will close in 5 seconds.");
					Sleep(5000);
					return 0;
				}
			}
			if(pg.show_info)
				info("Start waiting for "+int_to_string(num)+"ms.");
			if(!pg.program_check)
				Sleep(num);
			if(pg.show_info)
				info("Finish waiting.");
			continue;
		}
		if(operation=="P")
		{
			if(pg.show_info)
				info("Read pause in line "+int_to_string(for_time)+".");
			cout<<"Press any key to continue..."<<endl;
			if(!pg.program_check)
				getch();
			continue;
		}
		if(pg.show_error)
			error("Can't find right operation code in line "+int_to_string(for_time)+".");
		if(pg.program_check)
			continue;
		if(pg.show_warning)
			warning("Program will close in 5 seconds.");
		Sleep(5000);
		return 0;
	}
	bool es=false; 
	for(int i=0;i<key.length();i++)
	{
		if(keydown[key[i]])
		{
			if(pg.show_warning)	warning("Key '"+char_to_string(key[i])+"' was still down.");
			es=true;
		}
	}
	if(es)	wait(1);
	if(pg.debug_mode)
	{
		cout<<"show_info:\t"<<pg.show_info<<endl;
		cout<<"show_warning:\t"<<pg.show_warning<<endl;
		cout<<"show_error:\t"<<pg.show_error<<endl;
		cout<<"debug_mode:\t"<<pg.debug_mode<<endl;
		cout<<"ignore_error:\t"<<pg.ignore_error<<endl;
		cout<<"program_check:\t"<<pg.program_check<<endl;
	}
	if(pg.program_check || pg.debug_mode)
	{
		cout<<"Press any key to continue..."<<endl;
		getch();
	}
	return 0;
}
